package it.sd.wdc.spider.executer;

import it.sd.utility.URLBean;
import it.sd.utility.URLUtility;
import it.sd.utility.XMLWrapper;
import it.sd.wdc.spider.WdcSpiderException;
import it.sd.wdc.spider.WdcSpiderExecuterException;
import it.sd.wdc.spider.WdcSpiderRequest;
import it.sd.wdc.spider.htmlbrowser.WebClientException;
import it.sd.wdc.spider.repository.ScriptRepository;
import it.sd.wdc.spider.repository.WDCParams;
import it.sd.wdc.spider.repository.WDCService;
import it.sd.wdc.spider.repository.WDCStep;
import it.sd.wdc.spider.repository.template.SpiderNodeTemplate;

import java.util.Iterator;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ScriptExecuter  {

    private ScriptRepository repository = null;
    private Logger logger = Logger.getLogger(this.getClass());

    public ScriptRepository getRepository() {
		return repository;
	}

	public void setRepository(ScriptRepository repository) {
		this.repository = repository;
	}

	public void execute(WdcSpiderRequest ric) throws WdcSpiderException, WdcSpiderExecuterException {

        ScriptSession session= null; 
    
        logger.debug("nuova richiesta: " + ric);
        
        try {
        	// nuova sessione
        	session = new ScriptSession();

        	// recupero il servizio
        	WDCService service = repository.getService(ric.getIdService());
        	
        	// prendo il primo step da eseguire
        	WDCStep exeStep = service.getFirstStep();
        	
        	// valorizzo session con i parametri di input del servizio
        	addInput(session.getAttributes(), ric.getInput(), service);

            logger.debug("starting session: " + session);

        	
        	while (true) {
        		
        		if (exeStep == null) throw new WdcSpiderExecuterException("Errore esecuzione step: ");

        		// eseguo lo step
        		StepExecuter se = new StepExecuter(exeStep);
                logger.debug("executing: " + se);
        		String stepResult = se.execute(session, repository.getTemplateDocument(exeStep.getTemplate()));
        		logger.debug("Session ending: " + session);
        		
        		if (stepResult == null) {
        			break;
        		}
        		
        		// recupero lo step successivo
        		exeStep = service.getStep(stepResult);
        	}
        	
        	//estrae output
        	getOutput(session.getAttributes(), ric.getOutput(), service);
        	session.close();
        	

        } catch (WebClientException e) {
        	throw new WdcSpiderExecuterException(e.getMessage(), e);
        } catch (Exception e) {
        	throw new WdcSpiderExecuterException(e.getMessage(), e);
        } finally {
        }
    }

    /**
     * riporta i parametri presenti nel documento di input nella sessione dello script
     * come specificato dal parametro <input> del documento xml di richiesta
     * @param session sessione dello script
     * @param ric richiesta da evadere
     * @param s servizio richiesto
     * @throws Exception
     */
    private void addInput(XMLWrapper sessionAttribute , XMLWrapper xmlInput, WDCService s) throws Exception {

    	// recupero parametri di input
    	for (Iterator<WDCParams> iterator = s.iterateInputParams(); iterator.hasNext();) {
			WDCParams input = iterator.next();
    		String value = input.getValue();
    		if (value.startsWith("${")) {
    			value = xmlInput.extractStringValue(value);
    		}
    		sessionAttribute.addValue(input.getName(), value);
    	}
    	
    }
    
    private void getOutput(XMLWrapper sessionAttribute , XMLWrapper xmlOutput, WDCService s) throws Exception {

    	// recupero parametri di output
    	for (Iterator<WDCParams> iterator = s.iterateOutputParams(); iterator.hasNext();) {
			WDCParams output = iterator.next();
			String value = output.getValue();
			if (value.startsWith("${")) {
				NodeList nodelist = sessionAttribute.extractNodeValue(value);
				if (nodelist != null) {
					for (int j = 0; j < nodelist.getLength(); j++) {
						Node node = nodelist.item(j);
						if (node instanceof Element ){
							xmlOutput.addDoc((Element)node);
						}
					}
				}
			}
    	}
    	
    }
    
    public Document executeSinglePage(String method, String url, SpiderNodeTemplate template) 
    			throws Exception {
    	
     	WDCStep step = new WDCStep();
     	
     	URLBean urlBean = URLUtility.getUrlBean(url);
     	
     	step.setProtocol(urlBean.getProtocol());
     	step.setHost(urlBean.getHost());
     	step.setAction(urlBean.getAction());
     	for (Iterator<String> iterator = urlBean.iterateParams(); iterator.hasNext();) {
			String name= iterator.next();
    		WDCParams param = new WDCParams();
    		param.setName(name);
    		param.setValue(urlBean.getParam(name));
    		step.addInputParam(param);
    	}
    	
    	step.setDescription("test");
    	step.setId(0);
    	step.setMethod(method);
    	
    	ScriptSession session = new ScriptSession();
    	StepExecuter se = new StepExecuter(step);
    	se.execute(session, template);
    	
    	return session.getAttributes().getRootElement().getOwnerDocument();
    }
}